#include "parse_strain_names_from_vcf_header.h"

#include <boost/spirit/include/qi.hpp>
#include <boost/tr1/tuple.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>
#include <boost/spirit/home/phoenix/core/argument.hpp>
#include <boost/spirit/home/phoenix/algorithm.hpp>
#include <boost/spirit/repository/include/qi_flush_multi_pass.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/format.hpp>
	using boost::format;


#include "qi_parse_error_handler.h"

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

using std::string;
using std::cerr;
using std::vector;


phoenix::function<errorHandlerT> const errorHandler_header = errorHandlerT("VCF Header");


template <typename Iterator>
struct mini_vcf_header_grammar
: qi::grammar<Iterator, std::vector<string>()>
{
    mini_vcf_header_grammar(unsigned& curr_line_number_)
    : mini_vcf_header_grammar::base_type(vcf_header), curr_line_number(curr_line_number_)
    {
        using qi::lit;
        using qi::lexeme;
        using ascii::char_;
        using ascii::string;
        using namespace qi::labels;
        using qi::eoi;
        using qi::eol;

        header1       =  lit("#CHROM");
        header2       =  lit("POS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\tFORMAT");
        tab           =  lit('\t');
        strain_name   %= +(char_ - '\t' - eol);
        vcf_header    %=    //*   (
                            //        lit("##")                    >> 
                            //        qi::omit[ +(char_ - eol) ]  >>  
                            //        eol
                            //     ) 
                            //>>
                                header1 >> tab >>
                                header2 >> tab >>
                                strain_name % tab >>
                                -eoi;
                            //>> boost::spirit::repository::flush_multi_pass;
        
        header1              .name("Header Chromosome");
        header2              .name("Header Field Names");
        tab                  .name("Tab");
        strain_name          .name("Strain Name");

        qi::on_error<qi::fail>                               
        (vcf_header, ::errorHandler_header(_1, _2, _3, _4, phoenix::ref(curr_line_number)));
    }
    qi::rule<Iterator, void()>                          header1           ;   
    qi::rule<Iterator, void()>                          header2           ;   
    qi::rule<Iterator, std::string()>                   strain_name       ;   
    qi::rule<Iterator, void()>                          tab               ;   
    qi::rule<Iterator, std::vector<std::string>()>      vcf_header ;   
	unsigned& 											curr_line_number;
};


//boost::io::stream<io::mapped_file_source> input_file("/data/mus/visitors/lg/full/20100703-isec2-highconf-wref.vcf");


void parse_strain_names_from_vcf_header(std::istream& input_file, std::vector<std::string>& strain_names, unsigned& cnt_lines)
{
	//
	//  Parse strain names in header
	// 
	mini_vcf_header_grammar<string::const_iterator> vcf_header_grammar(cnt_lines);
	std::string str;
	while (getline(input_file, str))
	{
		cnt_lines ++;
		if (str.length() && str[0] != '#')
			throw std::runtime_error((format("No VCF Header with strain names found after %1$d lines\n[%2%s].") % cnt_lines % str).str());

		string::const_iterator str_beg = str.begin();
		string::const_iterator str_end = str.end();
		bool  ret = qi::parse(str_beg, str_end, vcf_header_grammar, strain_names);
		if (ret)
			break;
	}

	//
	//  Fail : no header found
	//
	if (strain_names.size() == 0) 
	{
		throw std::runtime_error("No strain names found.");
	}

	return;
}

